/*
 * @Description: UI组件依赖
 * @Author: HuiSir<273250950@qq.com>
 * @Date: 2021-12-17 11:03:27
 * @LastEditTime: 2025-09-18 10:58:11
 */

/* eslint-disable */
import { Ref } from "vue";
import { ElLoading } from "element-plus";

/**
 * 延时防抖
 */
export const debounce = <T extends any[]>(
  callback: (...args: T) => void,
  delay: number = 500
) => {
  if (typeof callback !== "function") {
    throw new TypeError("Expected callback to be a function");
  }
  if (typeof delay !== "number" || delay < 0) {
    throw new TypeError("Expected delay to be a non-negative number");
  }

  let timeout: any;
  return (...args: T) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      callback(...args);
    }, delay);
  };
};

/**
 * 节流(这里加入了loading用于按钮点击)
 * @param handler 待节流的函数
 * @param wait 节流时间间隔（毫秒）
 * @returns 节流处理后的函数
 */
export const throttle = (
  handler: (...args: any[]) => void,
  wait: number = 800,
  loading?: Ref<boolean>
): Function => {
  let lastTime: number = 0;

  return function (this: any, ...args: any[]) {
    const now = Date.now();

    // 立即执行条件：首次调用或超过等待时间
    if (now - lastTime >= wait) {
      handler.apply(this, args);
      lastTime = now;

      //loading
      if (loading) {
        loading.value = true;
        let timer = setTimeout(() => {
          loading.value = false;
          clearTimeout(timer);
        }, wait - (now - lastTime));
      }
    }
  };
};

/**
 * 前端关键字模糊搜索
 */
export const fuzzySearch = (listData: any[], inputVal: string) =>
  listData.filter((item) =>
    JSON.stringify(item).toLowerCase().includes(inputVal.toLowerCase())
  );

/**
 * 点击其他位置进行操作（如关闭弹窗）
 */
export const clickOtherPosToClose = (
  targetEl: HTMLElement[] | HTMLElement | NodeList,
  callback: () => void
) => {
  const listener = (e: Event) => {
    const isNodeList = targetEl.constructor.name === "NodeList";
    if (Array.isArray(targetEl) || isNodeList) {
      const contains = (
        isNodeList
          ? Array.from(targetEl as NodeList)
          : (targetEl as HTMLElement[])
      ).find((boxEl) => boxEl!.contains(e.target as HTMLElement));
      if (!contains) {
        callback();
        document.removeEventListener("click", listener, true);
      }
    } else if (!(targetEl as HTMLElement).contains(e.target as HTMLElement)) {
      callback();
      document.removeEventListener("click", listener, true);
    }
  };
  document.addEventListener("click", listener, true);
};

/**
 * 将字符串转为文档下载
 */
export const exportRaw = (name: string, data: string) => {
  //将字符串转存为文件
  //定义文件内容，类型必须为Blob 否则createObjectURL会报错
  const content = new Blob([data]);
  //生成url对象
  const urlObject = window.URL || window.webkitURL || window;
  const url = urlObject.createObjectURL(content);
  //生成<a></a>DOM元素
  const el = document.createElement("a");
  //链接赋值
  el.href = url;
  el.download = name;
  //必须点击否则不会下载
  el.click();
  //移除链接释放资源
  urlObject.revokeObjectURL(url);
};

/**
 * 深拷贝;
 * 支持循环引用，支持基本类型、对象、数组、Date、RegExp、Map 和 Set;
 * 不兼容低版本浏览器;
 * 保留原型链，无法克隆函数、DOM 节点;
 * @param value Object 需要拷贝的对象
 * @param hook Function 需要对拷贝后的对象做些事
 */
export const deepCopy = (
  value: Record<PropertyKey, any> | any[],
  hook?: (copyData: Record<PropertyKey, any> | any[]) => void,
  hash = new WeakMap()
) => {
  // 处理 null 和 undefined
  if (value === null || value === undefined) return value;

  // 处理基本数据类型
  if (typeof value !== "object") return value;

  // 处理日期
  if (value instanceof Date) return new Date(value);

  // 处理正则
  if (value instanceof RegExp) {
    const newRegExp = new RegExp(value.source, value.flags);
    newRegExp.lastIndex = value.lastIndex;
    return newRegExp;
  }

  // 处理循环引用
  if (hash.has(value)) return hash.get(value);

  // 处理数组和对象
  const clone: Record<PropertyKey, any> | any[] = Array.isArray(value)
    ? []
    : {};

  // 存储副本以处理循环引用
  hash.set(value, clone);

  // 处理 Map
  if (value instanceof Map) {
    const newMap = new Map();
    hash.set(value, newMap);
    value.forEach((v, k) =>
      newMap.set(deepCopy(k, void 0, hash), deepCopy(v, void 0, hash))
    );
    return newMap;
  }

  // 处理 Set
  if (value instanceof Set) {
    const newSet = new Set();
    hash.set(value, newSet);
    value.forEach((v) => newSet.add(deepCopy(v, void 0, hash)));
    return newSet;
  }

  // 处理普通对象和数组
  Reflect.ownKeys(value).forEach((key) => {
    clone[key as any] = deepCopy(value[key as any], void 0, hash);
  });

  typeof hook === "function" && hook(clone);
  return clone;
};

/**
 * 树结构递归转换
 * parentId list => children tree
 */
export const rawList2Tree = ({
  rawList,
  parentIdKey = "pid",
  ancestorId = null,
  childKey = "children",
}: {
  rawList: any[];
  parentIdKey?: string;
  /**
   * 祖先ID，默认为null
   */
  ancestorId?: string | null;
  childKey?: string;
}) => {
  if (rawList.length === 0) {
    return [];
  }

  let treeList: any[] = [];

  (function Recursion(level: number = 0, pid?: string) {
    const tempArr: Record<string, string>[] = [];
    for (let index = 0; index < rawList.length; index++) {
      const item = rawList[index];
      // 如果没有父id（第一次递归的时候）将所有父级查询出来
      // 这里认为 !item[parentIdKey] 或 等于topParentId 是最顶层
      if (
        pid
          ? item[parentIdKey] === pid
          : !item[parentIdKey] || item[parentIdKey] === ancestorId
      ) {
        // 树层级标记
        item.treeLevel = level;
        // 通过父节点ID查询所有子节点
        item[childKey] = Recursion(level + 1, item.id);
        tempArr.push(item);
      }
    }
    if (!pid) {
      treeList = tempArr;
    }
    return tempArr;
  })();

  return treeList;
};

/**
 * 递归转换
 * children tree =>  parentId list
 */
export const tree2RawList = ({
  treeList,
  idKey = "id",
  parentIdKey = "pid",
  childKey = "children",
}: {
  treeList: Record<string, any>[];
  idKey?: string;
  parentIdKey?: string;
  childKey?: string;
}) => {
  if (treeList.length === 0) {
    return [];
  }
  const copyTreeData = deepCopy(treeList);
  let rawList: Record<string, unknown>[] = [];

  (function Recursion(prevList: Record<string, unknown>[], pid: any = null) {
    const concatList = prevList.map((item: Record<string, any>) => {
      if (item[childKey] && item[childKey].length) {
        Recursion(item[childKey], item[idKey]);
        delete item[childKey];
      }
      item[parentIdKey] = pid;
      return item;
    });

    rawList = rawList.concat(concatList);
  })(copyTreeData);

  return rawList;
};

/**
 * 启动全屏
 * launchFullScreen(document.documentElement); // 整个网页
 */
export const launchFullScreen = (element: HTMLElement | any) => {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if (element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  } else if (element.msRequestFullscreen) {
    element.msRequestFullscreen();
  }
};

/**
 * 退出全屏
 */
export const exitFullscreen = () => {
  const doc: any = document;
  if (doc.exitFullscreen) {
    doc.exitFullscreen();
  } else if (doc.mozCancelFullScreen) {
    doc.mozCancelFullScreen();
  } else if (doc.webkitExitFullscreen) {
    doc.webkitExitFullscreen();
  } else if (doc.msExitFullscreen) {
    doc.msExitFullscreen();
  }
};

/**
 * 是否全屏
 */
export const isFullScreen = () => {
  return !!(
    (
      document.fullscreenElement || // 标准
      (document as any).webkitFullscreenElement || // Chrome/Safari/Opera
      (document as any).mozFullScreenElement || // Firefox
      (document as any).msFullscreenElement
    ) // IE/Edge
  );
};

/**
 * 取url名称
 */
export const getUrlFileName = (url: string) =>
  decodeURIComponent(url).split("/").pop()?.split("?")[0].split("#")[0] || "";

/**
 * 取url后缀（扩展）
 */
export const getUrlFileExt = (url: string) =>
  decodeURIComponent(url).split(".").pop()?.split("?")[0].split("#")[0] || "";

/**
 * 文件大小计算（将字节换算到kb及mb）
 */
export const fileSizeCompute = (bytes: number) => {
  const mb = Number((bytes / 1024 / 1024).toFixed(2));
  const kb = (bytes / 1024).toFixed(2);
  return mb > 1 ? mb + " MB" : kb + " KB";
};

/**
 * 全屏loading
 * by element-plus 使用close方法关闭
 */
export const showLoading = (text: string = "加载中") => {
  return ElLoading.service({
    text,
    lock: true,
    background: "rgba(255, 255, 255, 0.7)",
  });
};
